package EA;

public class TreeFramework extends Framework
{
  /** Array of possible oprerators the nodes in the tree can be. This
      is used when a new random tree is generated. The array contain both
      terminals and nonterminals. */
  public TreeGenomeContents[] possiblecontents;

  /** An array of probabilities for selecting each of the possible contents.
      By setting the values in the array you can control how often each
      of the operators/constants/variables should be selected.<br>
      NOTE: The sum of the entries in the array must be 1.
  */
  public double[] prob_possiblecontents;

  /** The array of terminal TreeGenomeContents. One of these terminals is
      selected when the depth of a random generated tree reaches maxdepth-1. 
  */
  public TreeGenomeContents[] possibleterminals;

  /** An array of probabilities for selecting each of the possible terminals.
      By setting the values in the array you can control how often each
      of the operators/constants/variables should be selected.<br>
      NOTE: The sum of the entries in the array must be 1.
  */
  public double[] prob_possibleterminals;

  /** The maximal depth of a random generated tree. */
  public int maxdepth;

  /** The framework contains a sample genome. This is cloned when random trees
      are generated. It is a way to get around the EXTREMELY CLUMSY way Java
      handles the polymorphy when writing some toplevel general code that 
      generates a random tree of subclassed treenodes. As I see it the only 
      alternative to this approach is for you to reimplement the random
      generation of trees in every subclass of TreeGenome.
  */
  public TreeGenome samplegenome;

  /** Create a new TreeFramework. This used when the framework is just used 
      to access implementation dependent variables.
  */
  public TreeFramework()
    {
      super();
    }

  /** Create a new TreeFramework. */
  public TreeFramework(Fitness ffunction)
    {
      super(ffunction);

    }

  /** Create a new TreeFramework.
      @param ffunction The fitness function you want to optimize.
      @maxDepth The maximal depth a random generated tree can have. 
      Use maxDepth = -2 to generate random trees with no bound.
      @param pos_contents Array of possible terminal and nonterminal contents. 
      This includes both
      operators (nodes with at least one son) and constants/variables (leaves
      with no sons).
      @param pos_terminals Array of possible terminal contents. 
      These should also appear in pos_contents.
  */

  public TreeFramework(Fitness ffunction, TreeGenome sample, int maxDepth, TreeGenomeContents[] pos_contents, TreeGenomeContents[] pos_terminals)
    {
      super(ffunction);
      setFields(sample, maxDepth, pos_contents, pos_terminals);
    }

  public TreeFramework(Fitness ffunction, TreeGenome sample, int maxDepth, TreeGenomeContents[] pos_contents, TreeGenomeContents[] pos_terminals, double[] prob_contents, double[] prob_terminals)
    {
      super(ffunction);
      setFields(sample, maxDepth, pos_contents, pos_terminals, prob_contents, prob_terminals);
    }

  /** Set the values of the fields. This method is needed because 
      TreeGenomeContents must have the TreeFramework they belong to
      and at the same time the constructor takes the possible contents.
  */
  public void setFields(TreeGenome sample, int maxDepth, TreeGenomeContents[] pos_contents, TreeGenomeContents[] pos_terminals)
    {
      maxdepth = maxDepth;
      samplegenome = sample;
      possiblecontents = pos_contents;
      possibleterminals = pos_terminals;

      { 
	double prob_contents, prob_terminals, cnt;

	uniformcontents = true;
	uniformcontents = true;
	prob_contents = 1.0/pos_contents.length;
	prob_terminals = 1.0/pos_terminals.length;
	sellower_poscontents = new double[pos_contents.length];
	selupper_poscontents = new double[pos_contents.length];
	prob_possiblecontents = new double[pos_contents.length];

	cnt = 0;
	for (int i=0;i<pos_contents.length;i++) {
	  prob_possiblecontents[i] = prob_contents;
	  sellower_poscontents[i] = cnt;
	  cnt = cnt+prob_possiblecontents[i];
	  selupper_poscontents[i] = cnt;
	}

	sellower_posterminals = new double[pos_terminals.length];
	selupper_posterminals = new double[pos_terminals.length];
	prob_possibleterminals = new double[pos_terminals.length];

	cnt = 0;
	for (int i=0;i<pos_terminals.length;i++) {
	  prob_possibleterminals[i] = prob_terminals;
	  sellower_posterminals[i] = cnt;
	  cnt = cnt+prob_possibleterminals[i];
	  selupper_posterminals[i] = cnt;
	}
      }
    }

  /** Set the values of the fields. This method is needed because 
      TreeGenomeContents must have the TreeFramework they belong to
      and at the same time the constructor takes the possible contents.
  */
  public void setFields(TreeGenome sample, int maxDepth, TreeGenomeContents[] pos_contents, TreeGenomeContents[] pos_terminals, double[] prob_contents, double[] prob_terminals)
    {
      samplegenome = sample;
      maxdepth = maxDepth;
      possiblecontents = pos_contents;
      possibleterminals = pos_terminals;
      prob_possiblecontents = prob_contents;
      prob_possibleterminals = prob_terminals;

      { 
	double cnt;

	uniformcontents = false;
	uniformcontents = false;
	sellower_poscontents = new double[prob_contents.length];
	selupper_poscontents = new double[prob_contents.length];

	cnt = 0;
	for (int i=0;i<pos_contents.length;i++) {
	  sellower_poscontents[i] = cnt;
	  cnt = cnt+prob_possiblecontents[i];
	  selupper_poscontents[i] = cnt;
	}

	sellower_posterminals = new double[prob_terminals.length];
	selupper_posterminals = new double[prob_terminals.length];

	cnt = 0;
	for (int i=0;i<pos_terminals.length;i++) {
	  sellower_posterminals[i] = cnt;
	  cnt = cnt+prob_possibleterminals[i];
	  selupper_posterminals[i] = cnt;
	}
      }
    }


  /* Used in the binary search of the operators. */
  protected double[] sellower_poscontents, selupper_poscontents;
  protected double[] sellower_posterminals, selupper_posterminals;
  protected boolean uniformcontents, uniformterminals;

  /* Perform binary search in a pair of arrays
     an index is returned if lower<=val and upper>=val
  */
  protected int findContents(double val, double[] lower, double[] upper, int l, int u)
    {
      int m = l+(u-l)/2;
      
      //Debugging:      System.out.println("l="+l+" m="+m+" u="+u+"   val="+val+" lower[l]="+lower[l]+" upper[u]="+upper[u]);
      if (lower[m]<=val && upper[m]>=val) {
	return m;
      }
      else if(upper[m]<=val) {
	return findContents(val, lower, upper, m+1, u);
      }
      else {
	return findContents(val, lower, upper, l, m);
      }
    }

  /** Select a random contents from possiblecontents based on the 
      probability distribution in prob_possiblecontents. This method is
      called when random trees are generated. (Constructor in TreeGenome)
  */
  public TreeGenomeContents selectRandomContents(Object thecontainer)
    {
      int cnumber = 0;

      if (uniformcontents) {
	cnumber = UsefulRoutines.randomInt(possiblecontents.length);
      }
      else {
	// Not uniform - search binary
	cnumber = findContents(Math.random(),sellower_poscontents, 
			       selupper_poscontents, 0, 
			       selupper_poscontents.length-1);
      }
      
      return possiblecontents[cnumber].randomContents(thecontainer);
    }

  /** Select a random terminal from possibleterminals based on the 
      probability distribution in prob_possibleterminals.
  */
  public TreeGenomeContents selectRandomTerminal(Object thecontainer)
    {
      int cnumber = 0;

      if (uniformterminals) {
	cnumber = UsefulRoutines.randomInt(possibleterminals.length);
      }
      else {
	// Not uniform - search binary
	cnumber = findContents(Math.random(),sellower_posterminals, 
			       selupper_posterminals, 0, 
			       selupper_posterminals.length-1);
      }
      
      return possibleterminals[cnumber].randomContents(thecontainer);
    }

  /** Override this to set termination condition. 
      @return boolean - false when the evaulation loop should end.
  */
  public boolean terminate()
    {
      return false;
    }

  /** Perform initialization. This method sets iteration to 0 and calls 
     the Framework_initialize_inner() method.
  */
  public void initialize()
    {
      iteration = 0;
      Framework_initialize_inner();
    }

  /** BETA style inner call for initialize(). <br>
      Override this in subclasses to perform initialization.
  */
  public void Framework_initialize_inner()
    {
    }

  /** Perform summarization. This method is called after the evolutionary loop
      and can be used to clean variables, store statistics etc.
  */
  public void summarize()
    {
    }

  /** Override this in your implementation. */
  public void selection()
    {
    }

  /** Override this in your implementation. */
  public void alter()
    {
    } 

  /** Override this in your implementation. */
  public void evaluate()
    {
    }

}
